//////////////////////////////////////////////////////////////////////////////////////////////////
//	File: AnimationManager.cpp
//
//	Author: Justin Maza
//
//	Purpose: Manages the animations of all game objects. Uses a string as an index into its
//			 map of animations
//////////////////////////////////////////////////////////////////////////////////////////////////
#include "AnimationManager.h"
#include "BaseCharacter.h"
#include "tinyxml\tinyxml.h"
#include "TileManager.h"
#include "GamePlayState.h"
#include "Messages\CreateCharacterMessage.h"
#include "Messages\CharacterDeathMessage.h"

AnimationManager* AnimationManager::pInstance = nullptr;

AnimationManager::AnimationManager(void)
{
	pTM = CSGD_TextureManager::GetInstance();
	frameArray = nullptr;
	DurArray = nullptr;
	ptPointArray = nullptr;
	triggerType = nullptr;
	triggerName = nullptr;
}


AnimationManager::~AnimationManager(void)
{
}

AnimationManager* AnimationManager::GetInstance(void)
{
	if(pInstance == nullptr)
		pInstance = new AnimationManager();

	return pInstance;
}

void AnimationManager::DeleteInstance(void)
{
	if(pInstance != nullptr)
		delete pInstance;
}

void AnimationManager::Render(BaseCharacter* pCharacter)
{
	if(pCharacter != nullptr)
	{
		float fScaleX, fScale;
		//RECT r = v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetCurrentFrame().rFrame;
		RECT r = v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetFrames()[pCharacter->GetAnimInfo()->currentFrame].rFrame;
		fScale = fScaleX = pCharacter->GetAnimInfo()->fScale;
		
		int nPosX = pCharacter->GetTile()->GetScreenPosX();
		//POINT anchor = v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetCurrentFrame().ptAnchor;
		POINT anchor = v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetFrames()[pCharacter->GetAnimInfo()->currentFrame].ptAnchor;

		if(pCharacter->GetAnimInfo()->bIsFlipped)
		{
			fScaleX *= -1;

			int width = r.right - r.left;

			nPosX += width * (int)fScaleX;

			anchor.x = width - anchor.x;
		}

		else
		{
			int width = r.right - r.left;
			nPosX += width * (int)fScaleX;
			anchor.x = width - anchor.x;

		}
		
		pTM->Draw(v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetImageID(), (int)TileManager::GetInstance()->GetScale()*(nPosX - (int)(anchor.x * fScaleX)+(int)(0.5f*TileManager::GetInstance()->GetTileSizeX())), (int)TileManager::GetInstance()->GetScale()*(pCharacter->GetTile()->GetScreenPosY() + (int)(anchor.y * fScale)), TileManager::GetInstance()->GetScale()*1.0f, TileManager::GetInstance()->GetScale()*1.0f, &r);
	}
}

void AnimationManager::Render(BaseCharacter* pCharacter, int posX, int posY, float fScale, bool isFlip)
{
	if(pCharacter != nullptr)
	{
		float fScaleX;
		RECT r = v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetFrames()[pCharacter->GetAnimInfo()->currentFrame].rFrame;
		fScaleX = fScale;
		POINT anchor = v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetFrames()[pCharacter->GetAnimInfo()->currentFrame].ptAnchor;

		if(isFlip)
		{
			fScaleX *= -1;

			int width = r.right - r.left;

			posX += width * (int)fScaleX;

			anchor.x = width - anchor.x;
		}

		else
		{
			int width = r.right - r.left;
			posX += width * (int)fScaleX;
			anchor.x = width - anchor.x;

		}
		
		pTM->Draw(v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetImageID(), posX - (int)(anchor.x * fScaleX), posY - (int)(anchor.y * fScale), fScaleX, fScale, &r);
	}
}

void AnimationManager::Update(BaseCharacter* pCharacter, float elapsedTime)
{
	if(pCharacter != nullptr)
	{
		/* */

		if(pCharacter->GetAnimInfo()->prevState != -1)
		{
			//if(!v_Animations[pCharacter->GetAnimInfo()->prevState]->GetIsPlaying())
				//v_Animations[pCharacter->GetAnimInfo()->prevState]->Play();

			if(!pCharacter->GetAnimInfo()->IsPlaying)
				pCharacter->GetAnimInfo()->IsPlaying = true;
		}

		unsigned int uFrameVecSize = v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetFrames().size();
		//static int	nIncrement = 1;
		v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->SetCurrentFrame(pCharacter->GetAnimInfo()->currentFrame);

		//if(!v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetIsPlaying())
		//	return;

		if(!pCharacter->GetAnimInfo()->IsPlaying)
			return;

		pCharacter->GetAnimInfo()->fTimeWaited += elapsedTime; // Detecting the change of time

		// Did the characer wait long enough to change frames?
		if(pCharacter->GetAnimInfo()->fTimeWaited > v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetFrames()[pCharacter->GetAnimInfo()->currentFrame].fDuration)
		{
			// keep the change!
			pCharacter->GetAnimInfo()->fTimeWaited -= v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetFrames()[pCharacter->GetAnimInfo()->currentFrame].fDuration;
			
			// Changing the character's frame;
			pCharacter->GetAnimInfo()->currentFrame += pCharacter->GetAnimInfo()->Increment;

			if(v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetIsOscillating())  // Used if the animation is oscillating
			{
				if(pCharacter->GetAnimInfo()->Increment > 0)
				{
					if((unsigned int)pCharacter->GetAnimInfo()->currentFrame >= uFrameVecSize - 1)
						pCharacter->GetAnimInfo()->Increment = -pCharacter->GetAnimInfo()->Increment;
				}

				else
				{
					if(/*v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetCurrentFrameIndex()*/pCharacter->GetAnimInfo()->currentFrame < 1)
					{
						//v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->SetCurrentFrame(0);
						pCharacter->GetAnimInfo()->currentFrame = 0;
						pCharacter->GetAnimInfo()->Increment = -pCharacter->GetAnimInfo()->Increment;

					}
						
				}
				
				
			}
			else
			{
				
				if(/*v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetCurrentFrameIndex()*/ (unsigned int)pCharacter->GetAnimInfo()->currentFrame >= uFrameVecSize)
				{
					if(v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetIsLooping())
						//v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->Reset();
						pCharacter->GetAnimInfo()->currentFrame = 0;
											
					else
					{
						//v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->Stop();
						pCharacter->GetAnimInfo()->IsPlaying = false;
						//v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->SetCurrentFrame(uFrameVecSize - 1);
						pCharacter->GetAnimInfo()->currentFrame = uFrameVecSize - 1;
					}
				}
			}
		}

		if((unsigned int)pCharacter->GetAnimInfo()->currentFrame < v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetFrames().size())
		{
			if(v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetFrames()[pCharacter->GetAnimInfo()->currentFrame].Triggertype != 0)
			{
				if(v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetFrames()[pCharacter->GetAnimInfo()->currentFrame].Triggertype == 1)
				{
					//GamePlayState::GetInstance()->MessageProc
					//CreateCharacterMessage* pMsg1 = new CreateCharacterMessage(false, KNIGHT, rand()% 3, rand()% 3);
				}

			}

			std::string temp = v_Animations[pCharacter->GetAnimInfo()->currentAnimation]->GetFrames()[pCharacter->GetAnimInfo()->currentFrame].Trigger;
			
			if( temp == "character_death")
			{
				if(!pCharacter->GetAnimInfo()->Dead)
				{
					CharacterDeathMessage* pMsg = new CharacterDeathMessage(pCharacter->GetType(), pCharacter);
					MessagingSystem::GetInstance()->SendMsg(pMsg);
				}
			}
			
			if(temp == "change_back")
			{
				switch(pCharacter->GetType())
				{
				case KNIGHT:
					pCharacter->ChangeState(0 + pCharacter->GetFacing());
					break;
			
				case ROGUE:
					pCharacter->ChangeState(16 + pCharacter->GetFacing());
					break;
			
				case WIZARD:
					pCharacter->ChangeState(10 + pCharacter->GetFacing());
					break;

				case ARCHER:
					pCharacter->ChangeState(23 + pCharacter->GetFacing());
					break;

				case BLACK_KNIGHT:
					pCharacter->ChangeState(29 + pCharacter->GetFacing());
					break;
				}
				
			}
		}
	}
}

void AnimationManager::AddAnimation(Animation* pAnimation)
{
	if(pAnimation)
	{
		/*map<int, Animation*>::iterator myIter = m_Animations.end();

		m_Animations.insert(myIter,<int, Animation*>(pAnimation);*/
		v_Animations.push_back(pAnimation);
	}
}

void AnimationManager::RemoveAnimation(Animation* pAnimation)
{
	if(pAnimation != nullptr)
	{
		vector<Animation*>::iterator iter = v_Animations.begin();

		for( ; iter != v_Animations.end(); ++iter)
		{
			if(*iter == pAnimation)
			{
				iter = v_Animations.erase(iter);
				break;
			}
		}
	}
}

void AnimationManager::RemoveAllAnimations(void)
{
	//vector<Animation*>::iterator iter = v_Animations.begin();

	//for( ; iter != v_Animations.end(); ++iter)
	//	{
	//		delete *iter;
	//	}

	//v_Animations.clear();
	unsigned int size = v_Animations.size();
	for(unsigned int i = 0; i < size; ++i)
	{
		delete v_Animations[i];
	}
	v_Animations.clear();
}

void AnimationManager::ShutdownAnimationManager(void)
{
	RemoveAllAnimations();
	if(frameArray != nullptr)
		delete[] frameArray;
	if(ptPointArray != nullptr)
		delete[] ptPointArray;
	if(DurArray != nullptr)
		delete[] DurArray;

	if(triggerType != nullptr)
		delete[] triggerType;
	if(triggerName != nullptr)
		delete[] triggerName;
}

bool AnimationManager::LoadAnimations(const char* filePath)
{
	// Create the TinyXML Document
	TiXmlDocument doc;

	// Attempt to load the document from the file
	if( doc.LoadFile( filePath ) == false )
		return false;


	// Access the root element ("players" list) in the document
	TiXmlElement* pRoot = doc.RootElement();
	if( pRoot == nullptr )
		return false;



	v_Animations.clear();

	TiXmlElement* pAniType = pRoot->FirstChildElement("AnimationType");
	
	if( pAniType == nullptr )
		return false;

	// Access child node of Animation Type
	while( pAniType != nullptr)
	{
		TiXmlElement* pAnimation = pAniType->FirstChildElement( "Animation" );

		currentRect = 0;
		Animation* info = new Animation();
		int Oscillate, Loop;
		const char* file = nullptr;

		if(pAniType->Attribute("File") == nullptr)
		{
			return false;
		}

		else
		{
			file = pAniType->Attribute("File");
		}

		if(pAniType->Attribute("NumFrames", &numElements) == nullptr)
		{
			return false;
		}
		
		if( pAniType->Attribute( "Oscillating", &Oscillate ) == nullptr )
		{
			return false;
		}

		if( pAniType->Attribute( "Looping", &Loop ) == nullptr )
		{
			return false;
		}
		
		int red, blue, green;

		if( pAniType->Attribute( "Red", &red ) == nullptr )
		{
			return false;
		}

		if( pAniType->Attribute( "Blue", &blue ) == nullptr )
		{
			return false;
		}

		if( pAniType->Attribute( "Green", &green ) == nullptr )
		{
			return false;
		}
		
		frameArray = new RECT[numElements];
		ptPointArray = new POINT[numElements];
		DurArray = new float[numElements];
		triggerType = new int[numElements];
		triggerName = new string[numElements];
		color = D3DCOLOR_XRGB(red, green, blue);

		while( pAnimation != nullptr )
		{
			// Read info
			//Animation info = { };
			
			

			// Read name
			//int ptop = pAnimation->ga;
			
			//strcpy_s( info.rFrame.left, 32, pTop );
			//cout << "PLAYER:\t" << info.szName << "\n\t";
		
			

			// Read attributes
			if( pAnimation->Attribute("left", &left) == nullptr )
			{
				return false;
			}

			if( pAnimation->Attribute("top", &top) == nullptr )
			{
				return false;
			}

			if( pAnimation->Attribute("right", &right) == nullptr )
			{
				return false;
			}

			if( pAnimation->Attribute("bottom", &bottom) == nullptr )
			{
				return false;
			}

			

			if( pAnimation->Attribute( "anchorX", &anchorX ) == nullptr )
			{
				return false;

			}

			if( pAnimation->Attribute( "anchorY", &anchorY ) == nullptr )
			{
				return false;

			}

			double Duration;
			if( pAnimation->Attribute( "Duration", &Duration ) == nullptr )
			{
				return false;

			}

			if(pAnimation->Attribute("TriggerType", &triggerType[currentRect])== nullptr)
			{
				return false;
			}

			string namer;
			if(pAnimation->Attribute("TriggerName") == nullptr)
			{
				return false;
			}

			else
			{
				namer = pAnimation->Attribute("TriggerName");
			}



			//************Saving data to our arrays for later access****************
			frameArray[currentRect].left = (long)left;
			frameArray[currentRect].top = (long) top;
			frameArray[currentRect].right = (long)right;
			frameArray[currentRect].bottom = (long)bottom;

			ptPointArray[currentRect].x = (LONG)anchorX;
			ptPointArray[currentRect].y = (LONG)anchorY;

			DurArray[currentRect] = (float)Duration;
			triggerName[currentRect] = namer;
			//triggerType[currentRect] = 
			//**********************************************************************

			currentRect++;
			pAnimation = pAnimation->NextSiblingElement( "Animation" ); // Move to the next Animation line of data
		}
		bool Oscillation, Looping;

		if(Oscillate == 1)
			Oscillation = true;

		else
			Oscillation = false;

		if(Loop == 1)
			Looping = true;

		else
			Looping = false;
		
		bool Os = false;
		bool loops = false;

		if(Oscillate == 0)
			Os = false;
		else
			Os = true;

		if(Loop == 0)
			loops = false;
		else
			loops = true;

		info->SetFramesM(file, frameArray, ptPointArray, numElements, DurArray, Os, loops, triggerType, triggerName, color); // Create the animation from the resulting data read in from our file
		v_Animations.push_back(info);																	 // Add the newly created animation to the Manager's vector

		if(frameArray != nullptr)
		{
			delete[] frameArray;
			frameArray = nullptr;
		}

		if(ptPointArray != nullptr)
		{
			delete[] ptPointArray;
			ptPointArray = nullptr;
		}

		if(DurArray != nullptr)
		{
			delete[] DurArray;
			DurArray = nullptr;
		}
		
		if(triggerType != nullptr)
		{
			delete[] triggerType;
			triggerType = nullptr;
		}

		if(triggerName != nullptr)
		{
			delete[] triggerName;
			triggerName = nullptr;
		}

		pAniType = pAniType->NextSiblingElement("AnimationType");										 // Access the next Animation Type line of data within the file
	}
	return true;
}

void AnimationManager::SaveAnimations(const char* filePath, vector<Animation*>& vAnimations)
{
	// Create the TinyXML Document
	TiXmlDocument doc;

	// Create the TinyXML Declaration (version)
	TiXmlDeclaration* pDec = new TiXmlDeclaration( "1.0", "utf-8", "" );


	// Write the declaration to the document
	doc.LinkEndChild( pDec );


	// Create the root node for the document ("Animations" list)
	TiXmlElement* pRoot = new TiXmlElement( "Animations" );
	doc.LinkEndChild( pRoot );


	// Loop through the vector to save individual Animations
	vector<Animation*>::iterator iter;
	for( iter = v_Animations.begin(); iter != v_Animations.end(); ++iter )
	{
	//	// Create a node for the Animation
	//	TiXmlElement* pAnimation = new TiXmlElement( "Animation" );
	//
	//	// Fill the node with Animation info
	//	pAnimation->SetAttribute( "left", iter->rFrame.left );
	//	pAnimation->SetAttribute( "top", iter->rFrame.top );
	//	pAnimation->SetAttribute( "right", iter->rFrame.right );
	//	pAnimation->SetAttribute( "bottom", iter->rFrame.bottom );
	//
	//	pAnimation->SetDoubleAttribute( "anchorX", iter->ptAnchor.x );
	//	pAnimation->SetDoubleAttribute( "anchorY", iter->ptAnchor.y );
	//	pAnimation->SetDoubleAttribute( "Duration", iter->fDuration );
	//
	//	pAnimation->SetAttribute( "Oscillating", iter->bOscillating );
	//	pAnimation->SetAttribute( "Looping", iter->bLooping );
	//
	//	pAnimation->SetAttribute( "Red", iter->red );
	//	pAnimation->SetAttribute( "Blue", iter->blue );
	//	pAnimation->SetAttribute( "Green", iter->green );
	//
		// SetDoubleAttribute for floating-point numbers!

		
		// Create a Text node for the Animation name
		//TiXmlText* pText = new TiXmlText( iter->szName );
		//pAnimation->LinkEndChild( pText );


		// Write the Animation node to the "Animations" node
	//	pRoot->LinkEndChild( pAnimation );
	}


	// Save the XML Document to the file
	//doc.SaveFile( filePath );
}